using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using WozLib.UI.Ink.SketchCommands;
using WozLib.WozCommands;
using WozLib.WozObjects;
using WozLib.UI.Ink;

namespace WOzTestUI.WozCommands
{
  // must be REDO before it can be UNDO
  public class WozDiscardChangesCommand : WozObjectWozCommand
  {
    [XmlAttribute("ScreenServerID")]
    public long ScreenServerID
    {
      get
      {
        return _ScreenServerID;
      }
      set
      {
        _ScreenServerID = value;
      }
    } long _ScreenServerID;

    // This is assigned when a REDO happens
    [XmlElement("SinceCommitServerCommandStack")]
    public Stack<WozLib.WozCommands.WozCommand> SinceCommitServerCommandStack
    {
      get
      {
        return _SinceCommitServerCommandStack;
      }
      set
      {
        _SinceCommitServerCommandStack = value;
      }
    } Stack<WozLib.WozCommands.WozCommand> _SinceCommitServerCommandStack;

    // This is assigned when a REDO happens
    [XmlAttribute("ClientChangeCommandQueue")]
    public Queue<SketchCommandCollection> ClientChangeCommandQueue
    {
      get
      {
        return _ClientChangeCommandQueue;
      }
      set
      {
        _ClientChangeCommandQueue = value;
      }
    } Queue<SketchCommandCollection> _ClientChangeCommandQueue;

    [XmlAttribute("IsClientCommittedScreen")]
    public bool IsClientCommittedScreen
    {
      get { return _IsClientCommittedScreen; }
      set { _IsClientCommittedScreen = value; }
    } private bool _IsClientCommittedScreen;

    /// <summary>
    /// Returns the WizardWindow that is the target of this command. Returns null if none exists.
    /// </summary>
    [XmlIgnore]
    public WizardWindow TargetWizardWindow
    {
      get
      {
        IServerObject iServerObject = TargetIServerObject;
        if (iServerObject is WizardWindow)
        {
          return iServerObject as WizardWindow;
        }
        else
        {
          return null;
        }
      }
      set
      {
        TargetServerID = value.ServerID;
      }
    }

    public WozDiscardChangesCommand()
    {
    }

    public WozDiscardChangesCommand(WizardWindow wizardWindow, long screenServerID, bool isClientCommittedScreen)
      : base(wizardWindow)
    {
      ScreenServerID = screenServerID;
      IsClientCommittedScreen = isClientCommittedScreen;
    }

    // an UNDO has to happen first so that we have a SinceCommitServerCommandStack and a ClientChangeCommandQueue to use for UNDO
    public override void Undo()
    {
      WizardWindow wizardWindow = TargetWizardWindow;

      if (SinceCommitServerCommandStack != null && 
        ClientChangeCommandQueue != null &&
        wizardWindow != null &&
        wizardWindow.ClientChangeCommandQueue != null)
      {
        // get a reference to the WozObjectContainer
        IServerObject iServerObject = ServerObjectRegistry.GetObject(ScreenServerID);

        // check that the IServerObject is in fact a WozObjectContainer
        if (iServerObject is SketchPad)
        {
          SketchPad woc = iServerObject as SketchPad;

          // restore the command queue state
          wizardWindow.ClientChangeCommandQueue = ClientChangeCommandQueue;

          // if this is the current committed screen, undo user changes
          if (IsClientCommittedScreen)
          {
            // !!! We need this to iterate LIFO so build a stack
            Stack<SketchCommandCollection> wccStack = new Stack<SketchCommandCollection>();
            foreach (SketchCommandCollection wcc in ClientChangeCommandQueue)
            {
              wccStack.Push(wcc);
            }



            // need this to come out LIFO
            foreach (SketchCommandCollection wcc in wccStack)
            {
              wcc.UndoAll();
            }
          }

          // restore the state of the WozObjectContainer WizardChanges
          woc.SinceCommitServerCommandStack = SinceCommitServerCommandStack;

          // roll back any wizard changes
          // !!! We need this to iterate invert the stack by pushing it into another stack
          Stack<WozCommand> wscStack = new Stack<WozCommand>();
          foreach (WozCommand wsc in SinceCommitServerCommandStack)
          {
            wscStack.Push(wsc);
          }

          // need this to come out FIFO
          foreach (WozCommand wsc in wscStack)
          {
            wsc.Redo();
          }

          if (IsClientCommittedScreen)
          {
            // now take it out of overlay mode
            woc.InOverlayMode = false;
          }
        }
      }
    }

    public override void Redo()
    {
      WizardWindow wizardWindow = TargetWizardWindow;

      if (wizardWindow != null &&
        wizardWindow.ClientChangeCommandQueue != null)
      {
        // get a reference to the WozObjectContainer
        IServerObject iServerObject = ServerObjectRegistry.GetObject(ScreenServerID);

        // check that the IServerObject is in fact a WozObjectContainer
        if (iServerObject is SketchPad)
        {
          SketchPad woc = iServerObject as SketchPad;

          // make sure a SinceCommitServerCommandStack exists
          if (woc.SinceCommitServerCommandStack != null)
          {
            // save the state of the WozObjectContainer WizardChanges
            SinceCommitServerCommandStack = woc.SinceCommitServerCommandStack;

            // (we don't need any locks because any changes will just be added to the old stack or the new one)
            woc.SinceCommitServerCommandStack = new Stack<WozCommand>();

            woc.Suspended = true;

            // redo any wizard changes
            // !!! We need this to iterate LIFO
            foreach (WozCommand wsc in SinceCommitServerCommandStack)
            {
              wsc.Undo();
            }

            woc.Suspended = false;

            // save the command queue state
            ClientChangeCommandQueue = wizardWindow.ClientChangeCommandQueue;

            // clear the command queue by changing the reference
            // (we don't need any locks because any changes will just be added to the old queue or the new one)
            wizardWindow.ClientChangeCommandQueue = new Queue<SketchCommandCollection>();

            // if this is the current committed screen, apply user changes
            if (IsClientCommittedScreen)
            {
                // !!! We need this to iterate FIFO
              foreach (SketchCommandCollection wcc in ClientChangeCommandQueue)
                {
                  wcc.RedoAll();
                }

              // now put it in overlay mode
              woc.InOverlayMode = true;
            }
          }
        }
      }
    }
  }
}
